视觉格式化模型 - CSS 2.2
视觉格式化模型 Visual formatting model:浏览器对于DOM树的可视化处理方式。
本文基于 CSS 2.2 的模型概念,不涉及 CSS 3 相关的知识(比如,flex排版,grid排版)。
1. 简介
在视觉格式化模型中,DOM树中的每个元素,基于盒模型,会生成一个或多个盒。然后根据排版规则,形成页面。
排版规则的基本参考因素:
- box dimensions and type.
- 盒子尺寸(content, padding, border, margin)、盒子类型(各种 block )。
- position scheme (normal flow, float, and absolute position).
- 盒子的定位方式,即受 position 属性影响。
- relationships between elements in the document tree.
- DOM 中,元素之间的结构。
- external information (e.g., viewport size, intrinsic dimensions of images, etc.).
- 外部信息(例如,视口大小、图像的固有尺寸等)。
1.1 基本概念
1.1.1 引入
- leyout
- 排版。有翻译为布局的,但称之为 'CSS排版' 更为合适。
- Tag
- 标签。代表源代码中一对尖括号
<>
,在HTML的代码中书写的都是 <标签>。
- 标签。代表源代码中一对尖括号
- element
- 元素。通过一对标签表示出来的,添加了 HTML 语义。
<html> </html>
就是一对标签,形成了一个有意义的 HTML 元素。
- 元素。通过一对标签表示出来的,添加了 HTML 语义。
- box
- 盒。在页面中,所有元素都被渲染成一个或多个矩形盒子。是排版和渲染的基本单位。
1.1.2 flow
flow:流,是页面排版的基本规则。
基本的定位方案有:
- normal flow: 正常流。格式化上下文的方式排版内容(块级、行内级)。即通常来讲,默认从左至右铺满盒子,再由上至下排列。
- flow:浮动。当一个元素被定位浮动时,它会先按照正常流来摆放,然后向该行的左/右 “挪动”,会脱离正常流。
- absolute:广义的绝对定位(absolute + fixed),当一个元素被绝对定位时,会脱离正常流。
in flow:流内。处在当前正常流中的元素,被称之 为流内。
out of flow:流外。脱离当前正常流中的元素,被称之为流外。
-
当一个元素的定位方案是 浮动、绝对定位(absolute & fixed) 或 根元素,那它会在当前正常流(标准文档流)之外,此时该元素可视为流外元素。
- 注1:根元素(
<HTML>
元素),是一个最大的流外元素。在通常情况下,标准文档流的概念被定义为<html>
元素下的正常流。“脱离标准文档流” 就是脱离了根元素下的正常流。 - 注2:流外、流内是相对而言的。如果 A 元素处在正常流区域 X 中。以区域 X 为参考环境下,A 元素是流内元素;以区域 Y 为参考环境下,A 元素是流外元素。
- 注1:根元素(
-
每一个流外元素的内部,都会产生一个新的流。
-
如果 A 元素内部的正常流中,有一个 B 元素脱离 A 内部的正常流,则 B 元素被当成流外元素,B 元素内正常流下的内容已不属于 A 元素的流。B 元素属于 A 元素的流。
1.1.3 定位
CSS Flow Layout
- CSS 流式排版。就是'正常流'。
Normal Flow
- 正常流。元素在页面中的基本排版规则。
- 是CSS中对于元素的排版方式。元素渲染成众多盒子,然后通过流式排版,排版到页面中。
- 当一个盒子,不浮动 (float: none),不是绝对定位 (position: static / relative) 时,排版为正常流。
- 在正常流的块级格式化上下文中,盒子会垂直依次排列;
- 在正常流的行内级格式化上下文中,盒子会水平依次排列。
float 属性
- 浮动。在浮动定位中,浮动盒子会浮动到 当前行 的开始或尾部位置。
- 当一个盒子,浮动 (float: left / right),且不是绝对定位 (position: static / relative) 时,为浮动定位。此时所在行的行盒 (inline box),会自动缩小,以 “让开” 浮动盒子的位置。
clear 属性
-
指定一个元素是否必须移动 (清除浮动后) 到在它之前的浮动元素下面。适用于浮动和非浮动元素。
- 针对 float 元素:可以让该 float元素不是按照 平移 的方式排到其他浮动元素后,而是另起一行,排到第一个。
- 针对 非 float 元素:可以让该元素的文本 / 内联元素,不是环绕 float 元素。而是将该元素的边框边界,全部移到 float元素的下方。同时会发生外边距折叠。
-
none:不清除浮动
-
left / right:清除左/右浮动,向下移动。
-
both:左右浮动全部清除,向下移动。
absolute 属性
- 泛指:绝对定位。是 position 中的两个属性:absolute,fixed。
- 在绝对定位中,盒子会完全从当前流中移除,并且不会再与其有任何联系(注:此处仅指定位和位置计算,而绝对定位的元素在文档树中仍然与其他元素有父子或兄弟等关系),其位置会使用 top、bottom、left 和 right 相对其 包含块(后文有解释)进行计算。
- 绝对定位是一个泛指,其实细分下来,有两个定位方式:
- position: absolute,绝对定位。是相对于直系父元素(非 static的元素)来定位,把这个父元素当成包含块。
- position: fixed,固定定位。是相对于 视口 的定位方式,也就是说该元素的包含块是 视口 大小。
The stacking context
- 层叠上下文。
- 通过 z-index修改的,位于z轴视角下的上下文。
viewport
- 视口。
- 浏览器会通过视口将页面内容呈现给用户。换句话说,视口通常可以理解为浏览器窗口大小(或可视区域)。
- 视口会涉及到 intial containing block 初始包含块 相关概念。
convas
- 画布。
- 画布是文档最终呈现的页面(比如,一个word文档的实际页面大小就是 convas(画布),显示屏的大小就是 viewprot(视口)。
- 如果视口比画布尺寸要小,浏览器会提供滚动机制 / 缩放机制。
containing block
- 包含块(后文会有一节详解)。
- 此概念是针对 position 属性而来的。浏览器通过 position 属性的参照物(包含块),决定该元素生成盒的位置。
- 元素的 position 值是 'relative' or 'static':包含块是最近的 block container box 的边界。
- 元素的 position 值是 'fixed':包含块是初始包含块,即视口大小。
- 元素的 position 值是 'absolute':包含块是最近的、非static的、block container box 的边界。
包含块简单来说,就是可以包含其他盒子的块。而问题讨论的核心是以 一个子盒子自身 为视角,确定它的直接的父包含块是哪一个,以此为参考来渲染子盒子。
盒子会根据它的包含块的边界,来定位自己的位置。但是盒子不会被包含块的范围限制,如果盒子跑到包含块的外面,称为溢出(overflow)。
initial containing block
- 初始包含块
- 根元素所在的包含块,大小和视口的大小相当。
2. 盒类型
浏览器会通过语义,把HTML标签识别为一个个元素(elements)。然后浏览器又会把元素渲染成各种类型的盒子,最后,根据盒类型盒排版规则,把它们渲染到页面中,呈现给用户。下面会介绍 CSS 2.2 中,所有类型的盒子。
- 包含块类型,是通过 position 属性确定的;
- 盒类型,是通过 display 属性确定的。
2.1 盒模型
外部显示类型
盒模型中,外部显示类型,决定了该盒子是块级盒子,还是内联盒子(block box,inline box)
内部显示类型
决定盒子内部是如何布局的。
- 默认:正常流。
- display: flex 弹性。外部显示类型为block,内部显示类型为 flex。该盒子的所有直接子元素都会成为flex元素,会根据 弹性盒子(Flexbox)规则进行布局。
- display: grid 网格。外部和特性基本同上,只是内部换成 grid盒子。
2.1 名词总结
为达成认知统一,以下英文为 CSS 官方标准中出现的名词,中文为本文中对应的翻译。
-
block-level elements 块级元素
-
inline-level elements 内联元素
-
block-level box 块级盒子
-
inline-level box 内联级盒子
-
block box 块盒子
-
inline box 行盒子
-
block container box 块级容器盒子
-
anonymous box 匿名盒子
-
anonymous block box 匿名块级盒子
-
anonymous inline box 匿名内联盒子
-
element 元素
-
principal box 主体盒
-
principal block-level box 主体块级盒
-
principal inline-level box 主体内联级盒
-
replaced element 可替换元素
-
non-replaced element 不可替换元素
2.2 盒的生成
开头讲过,浏览器根据元素的特性,来生成对应类型的盒。具体过程如下:
通常情况下,浏览器根据某元素的 display 属性,生成一个 主体盒(principal box),这里是一对一关系,一个元素会生成一个主体盒。这个主体盒中包括了元素中的内容以及它的其他子盒。
- list-item 元素例外。浏览器在生成它的 主体盒 的同时,还会生成一个 标记盒(marker box),类似列表项前的数字 / 小圆圈 / 小点点。标记盒的定位方案与主体盒有关。
所以,不是所有的元素都只生成一个主体盒,有可能会生成多个盒(比如生成标记盒)。
结论, 与主体盒相关的元素,比如标记盒、子元素生成的盒子、主体盒中的内容等,其定位都与主体盒有关。
2.3 Block & Inline
盒的生成,从大的范围来讲,有两大类盒子: 块(block),内联(inline)。
上文提到过:浏览器根据某元素的 display 属性,生成主体盒(principal box)。根据排版规则的不同,又划分为两种主体盒:
- 块类型的元素(block-level element),生成主体块级盒(principal block-level box)。
- 内联类型的元素(inline-level element),生成主体内联盒(principal inline-level box)。
2.4 Block 系列
在文档中有四个概念与 块(block) 相关:block-level element、block box、block-level box 和 block container box。这四个概念不是单纯的相交或互斥,也不是单纯的 A 是 B 的子集,下面会分别介绍这四个概念。
涉及到的名词有:
- block-level element 块级元素
- block box 块盒
- block-level box 块级盒
- principal block-level box 主体块级盒
- block container box 块级容器盒
先说结论:block box = block-level box + block container box 即,一个块盒:既要满足块级盒的特性,又要满足块级容器盒的特性。
2.3.1 Block-level element
块级元素,是在 HTML 层面来讲述的。即把标签 “语义” 化为一个有意义的 “元素”。然后可以被 CSS 选择器所识别。在 CSS 的视角看来,HTML代码中的 “标签” 都被识别为了 “元素”。而块级元素,就是元素中那些被划分为块级的元素。
如何把元素划分为块级?
- 通过 display 属性设置为 'block'、'list-item'、'table' 的元素都是块级元素。
块级元素不直接用于格式化上下文或排版,这只是一个针对元素本身的属性概念。
一个块级元素会被格式化成一个块级盒(例如文章的一个段落),然后会参与排版,默认按照垂直方向依次排列。
2.3.2 Block-level box
块级盒的定义:
- 从来源上:块级盒是由块级元素生成而来的盒子;
- 从自身定位:块级盒是参与块级格式化上下文(block formatting context)的盒子;
- 从结果上:它最终的排版位置,是被一个 BFC 环境所影响的。
一个块级元素,至少会生成一个块级盒,但是有时候会生成多个(list-item列表元素,上文提到)。
principal block-level box
定义:每个块级盒子都会参与到块格式化上下文(block formatting context)的创建,而每个块级元素都会至少生成一个块级盒子,即主块级盒子(principal block-level box)。列表项元素会额 外生成标示盒,放置项目符号。
主块级盒是由块级元素生成的盒。定义这样一个概念的目的是在于,当涉及到定位方案时(position 属性相关)确定子元素生成盒的定位位置,通常会与主体块级盒相关。也就是会涉及到上文 containing block(包含块)的概念。(个人认为,包含块和主体块级盒的概念是重复的)
简单来说,主体块级盒和包含块的概念,就是为 position 定位方案服务的。
2.3.3 Block container box
粗略来看,块容器盒顾名思义,它可以容纳块级概念的元素。认清这个概念的定义,或者说区分这个概念的方法,就是要先判断一个盒子包含的东西是否是块级的,它是不是一个 容器 。
块容器盒这个概念,不是为了参与当前块的排版和定位而定义的,只是为了描述当前盒和其后代子盒之间的关系,为了确定后代盒的排版和定位。从这一点上,block container box 概念,和 containing block 相同。
块容器盒的定义:
- 从盒子包含物的内容上看:盒子里面可以有正常流,它就是块容器盒(block container box)。
- 从盒子包含物的类型上看:块容器盒只包含 block-level box 或者只包含 inline-level box。
- 如果是只包含 inline-level box,则该块容器盒内部,是一个 IFC 内联格式上下文。
- 从自身角度:块容器盒可以容纳正常流(块容器盒里面可以是 BFC)。
判断:什么类型的盒子,是 block container box?
一个粗略的判断依据:这个盒子中,可不可以放下一个正常流?如果可以放下一 个正常流,那就是 block container box,同时这个盒子会创建一个 BFC(后文会讲);如果不可以放置正常流(替换元素、类似 table-row 只能放特殊盒子),就不是 block container box。
- display 属性的值是以下参数时,该元素均是 block container(以下均必须是 non-relpaced element 不可替换元素):
- block:块
- inline-block:内联块
- list-item:列表
- table-cells:表格子项
- table-captions:表格标题
- flex item:(CSS 3)flex 盒子项
- grid cells:(CSS 3)grid 盒子项
- display 属性的值是以下参数时,该元素不是 block container:
- table-row:这里面要存放 table-cell,不能放正常流。
- flex:这里面要存放 flex子项,不能放正常流。(CSS 3)
- grid:这里面要存放 grid子项,不能放正常流。(CSS 3)
- ...
- 元素类型是 replace element,即替换元素,均不是 block container box。replaced element 的内容不归 CSS 渲染,这些内容最后是要被其他内容物替换的,该元素本身就是一个空元素,没有内容。所以自然不是一个可以放块级盒子的 “容器”。 替换元素不可以理解为一个 “容器”,而是应当理解为一个 标记位。
2.3.4 Block box
既满足了(block-level box)的要求,又满足了(block container)的要求的盒子,是块盒(block box)。
- 块级盒 block-level box:描述了元 素与其父元素和兄弟元素之间的行为。
- 块容器盒 block container box:描述了元素跟其后代之间的行为。
也就是说,这个盒子既能放得下 BFC,也可以放在一个BFC中。
-
从外部显示来讲,这个盒子是一个 block-level box:它能参与到一个 BFC 中。
-
从内部显示来讲,这个盒子是一个 block container box:它的后代盒子可以是块级的,可以放 BFC 。
-
有些块级盒不是块容器盒:表格 table;
-
有些块容器盒不是块级盒:非替换的行内块 non-replaced inline-block、非替换的表格单元格 non-replaced table-cell。
2.3.5 Anonymous block box
匿名块盒。
如果一个块容器盒(block container box 代码的 div)里面有一个块级盒子(block-level box 代码的 p),那么这个快容器盒中的其他元素,就会都被强制渲染为块级盒子(代码 Some text 生成了一个匿名块盒)。
<div>
Some text
<p>More text</p>
</div>
引申:Anonymous inline box
匿名行内盒
如果一个块容器盒(block container box):里面没有块级盒(block-level box),但是里面有行内盒(inline box),那么这个快容器盒中的其他元素(只剩text文本元素了),就会都被强制渲染为行内级盒子( text 生成了一个匿名行内盒)。
<p>Some <em>emphasized</em> text.</p>
总结
匿名盒子(Anonymous box)分为:Anonymous block box 和 Anonymous inline box。
直接暴露在一个 block container 中的文本,不是被包装成匿名块盒子,就是被包装成匿名行内盒子。
同时指出,CSS 无法直接选中匿名盒子。对匿名盒子的渲染通常会参考父元素的样式。也就是说,此时所有可继承的 CSS 属性值都为 inherit
继承,而所有不可继承的 CSS 属性值都为 initial
初始。
2.3.6 Q & A
- 关于 block-level box 和 block container box:
这两个盒子不是互斥的,即这两个盒子是有交集的。这个交集就是 block box。
-
什么情况下,一个 block-level box,不是 block container box:
- 这个问题换句话说:一个块级盒子,在什么情况下,它不能包含其他块级。
- 答:如果这个盒子是替换元素时,不能作为 block container box,不能作为 '容器'。
- 原因:replaced element 其内容不归 CSS 渲染,最后是要被其他内容物替换的。所以自然不是一个可以放块级盒子的'容器'。 替换元素不是'容器'。
-
举例:阐明 block box、block-level box 和 anonymous block-level box 的关系(MDN)
- 'div' 和 'p' 元素,都是默认的 display = 'block'。
<div>Some inline text <p>followed by a paragraph</p> followed by more inline text.</div>
- CSS无法获取两个匿名块级盒所在的元素,所以这两个的样式,是通过继承 'div'而来。如果没有为他们指定 'background-color',他们就具有默认的 透明背景。
2.4 Inline 系列
以下翻译中,‘行内’ 可以等效替换为 ‘内联’。
2.4.1 Inline-level element
定义:display属性设置为 'inline', 'inline-block', 'inline-table',这些元素都是行内级元素。
行内级元素不直接用于格式化上下文或排版,这只是一个针对元素本身的属性概念。行内级元素的内容可以分布在多行显示。
2.4.2 Inline-level box
行内级盒的定义:
- 从来源上:行内级盒是由行内级元素生成而来的盒子;
- 从结果上:行内级是参与行内级格式化上下文(inline formatting context)的盒子。
2.4.3 Inline box
行内盒。
行内盒是行内级盒子中,由不可替换元素 (non-replaced element) 生成的、且 dislpay 值是 'inline' 的盒子。它参与行内格式化上下文。是 Inline-level box 的一个子集。一个行内盒的内部,是一个 IFC 环境,即行内盒可以容纳内联级盒子。
以下生成的盒子,是 Inline-level box,但不是 Inline box,他们都是 atomic inline-level box:
- 替换的内联 级元素(replaced inline-level elements):比如内联的 img。其内部无法放任何盒子。
- 内联块元素(inline-block element):其内部是一个 BFC,而不是 IFC。
- 内联表格元素(inline-table element):里面是表格的特殊格式。
2.4.4 atomic inline-level box
原子行内级盒。
是行内级盒子的子集。它不是不参与行内级格式化上下文,而是更特殊:因为原子行内级盒的内容不会被拆分成多行显示。所以如果遇到 atomic inlne-level box,并不会像传统的 IFC 处理方式一样,从左到右自然的 “挤满” 一整行,再重新开启下一行。而是会作为一个不可分割的整体,如果本行所剩空间放不下它时,便会 “提前” 排放到下一行,本行剩余空间的会闲置不用。
可以看到,上图粉色框内是一个 IFC 环境。而中间蓝色框被设置为:"display : inline-block" 所以内部会形成一个 BFC。这个盒子作为一个不透明的整体,参与到所处的 IFC 环境中,上下和右边,都被蓝色盒子的 width 和 height 的设置而挤开了。
**问题:**既然 inline-level box 和 inline box 都表示参与行内级格式化上下文的行内级盒子,为什么要区分出两个概念?
- 两者区别:
- inline-level box 是站在这个盒子自身的角度,判断这个盒子是不是可以参与行内级格式化上下文。
- inline box 是站在盒子后代的角度,判断这个盒子的后 代可否参与行内级格式化上下文,类似 'block container box' 是 '容器' 的这个思路,inline box 也是一个行内级的 '容器',它可以把一个 IFC 放在其中(inline-level box 和 text)。
- 从这个角度来讲: inline-block element 属性创建的盒子,就属于 inline-level box 因为他可以参与行内格式化上下文;而它不属于 inline box 因为它的后代不可以是行内级的(内部是 BFC),或者说可能就没有后代(可替换元素,比如一个 img)。
An inline box is one that is both inline-level and whose contents participate in its containing inline formatting context. A non-replaced element with a 'display' value of 'inline' generates an inline box. Inline-level boxes that are not inline boxes (such as replaced inline-level elements, inline-block elements, and inline-table elements) are called atomic inline-level boxes because they participate in their inline formatting context as a single opaque box.
从这里可以看到,atomic inline-level box 是一个 inline-level box。也就是说, inline-level box 由两种 box 组成:atomic inline-level box 和 inline box。
- atomic inline-level box:作为内部 “不透明” 盒子参与内联格式化上下文。
- 由三种元素组成:可替换的 inline-level element、inlne-block element、inline-table element。
- inline box:参与内联格式化上下文。是由一个内联的、不可替换的元素构成。
所以,Inline box 和 atomic inline-level box 的概念是互斥的。
9.2.2 Inline-level elements and inline boxes
Inline-level elements are those elements of the source document that do not form new blocks of content; the content is distributed in lines (e.g., emphasized pieces of text within a paragraph, inline images, etc.). The following values of the 'display' property make an element inline-level: 'inline', 'inline-table', and 'inline-block'. Inline-level elements generate inline-level boxes, which are boxes that participate in an inline formatting context.
An inline box is one that is both inline-level and whose contents participate in its containing inline formatting context. A non-replaced element with a 'display' value of 'inline' generates an inline box. Inline-level boxes that are not inline boxes (such as replaced inline-level elements, inline-block elements, and inline-table elements) are called atomic inline-level boxes because they participate in their inline formatting context as a single opaque box.
2.4.5 line box
行盒。
包含来自同一行所有盒的矩形区域叫做行盒(line box)。所有行内级元素会从左至右依次排列,遇到包含块的边界,则再下一行继续排列。渲染的结果中,每一行就是一个 line box。
line box 的概念和 block box 相当。
inline box 主要是为了看其内部是不是一个 IFC 环境而定义的。
区分: line box 和 Inline box
- line box:
- 行盒。一个排版的概念,不会参与实际渲染。同一行的所有行内盒,会被一个行盒包括。无法通过 HTML 标签,或者 CSS 去定义。是一个在最终排版结果上看到的盒。
- Inline box:
- 行内盒。行内盒是行内级盒子中,由不可替换元素(non-replaced element) 生成的,且 dislpay 值是 'inline'的盒子。它参与行内格式化上下文。是 Inline-level box 的一个子集。
从结果上看,在一个行内格式化上下文中(水平书写模式):
- 所有 line box 垂直排列;
- 同一个 line box 中,所有的行内级元素水平排列。
参考
3. 元素的替换性
(Empty elements / Void elements)不是所有的元素都有开始和结束标签。有些元素只有一个标签,在这个元素位置上,浏览器会嵌入一些额外的内容。这些元素如果没有被正确的替换,则不会在网页中显示任何东西。
- 比如
<img />
标签,最终该位置会被一个图片(或一段文字)替换。 - 比如
<input />
标签,根据它不同的 type 属性值,所在位置会被替换为不同的元素:输入框、单选 / 多选 等等。
可 替换元素 可以任意设置 width height margin padding 等的值。改变替换后元素的尺寸。
不可替换元素 不可以随意修改,而是要通过 CSS 中 block 和 inline 性质来判断。
3.1 non-replaced element
不可替换元素。
可以理解为正常的元素。绝大多数元素都是不可替换的:元素的内容通过CSS渲染,然后在网页中展示,而不是被替换成其他内容。
3.2 replaced element
替换元素。
在CSS中,有些元素的展现效果不是由 CSS 来控制,它们是外部对象。通过浏览器识别元素类型,来替换该元素内容,而不是通过 CSS 来渲染。换句话说,该元素原本是一个空元素,没有内容。浏览器根据标签含义,替换成一个相应内容。
进一步解释,CSS 只可以调整替换元素的位置,而不能修改元素内容的样式。
-
HTML 规范上,替换元素有:
img, input, iframe, select, textarea, object, video, audio, canvas, embed
-
MDN 上典型的替换元素有:
<iframe>
、<video>
、<embed>
、<img>
(最常见)
-
MDN 上特定情况下,是替换元素的有:
<option>
、<audio>
、<canvas>
、<object>
、<applet>
(不推荐使用)
此外,通过 CSS 属性值 content 生成的对象,成为 匿名替换元素 anonymous replaced element。这就是一个替换元素。
h1 {
contnet: url(logo.png)
}
content 生成的替换元素:
- 生成的文本无法选中、无法复制、无法被屏幕阅读设备读取、无法被搜索引擎抓取;
- 原本的文字信息没有被替换、替换的只是视觉层。
3.3 Intrinsic dimensions
固有尺寸。
所有替换元素,都是具有固定尺寸的,即该元素的宽高由其自身标签类型定义,不受周围元素影响。
通常情况下,行内元素(Inline-level element)是无法设置width height 属性的,但是可替换的行内级元素(replaced inline-level element),是可以修改 width height 属性。如果不做修改,其默认值是元素的固有尺寸。
- 比如:display 属性值为 'inline' 的
<img>
可以设置宽高,但是<span>
不可以设置宽高。
4. containing block
包含块。
4.1 定义
定义一个 '包含块' 概念的目的:确定一个元素的尺寸和位置,有时候会需要一个参照物,而这个参照物是就是它的包含块。
4.2 确定包含块
确定一个元素的包含块的过程完全依赖于这个元素自身的 position
属性(与 position 知识重合):
- 'static', 'relative', 'sticky'(CSS 3):包含块是该元素的直系祖先块元素的内容区的边缘组成。
- 直系祖先块元素(inline-block, block, list-item elements)
- 内容区(盒的 content)
- 'absolute':包含块是由它的直系的、非
static
的祖先元素的内边距区的边缘组成。- 非'static'的祖先元素(position: fixed, absolute, relative, sticky)
- 内边距区(padding)
- 'fixed':通常情况直接理解为是视口,下面是详细情况。
- 连续媒体(continuous media)的情况下:包含块是适口viewport;
- 分页媒体(paged media)下的情况下:包含块是分页区域(page area)。
- (太细了先不管)'absolute', 'fixed':如果满足以下条件,会是直系父元素的内边距区的边缘组成。
transform
或perspective
的值不是none
will-change
的值是transform
或perspective
filter
的值不是none
或will-change
的值是filter
(只在 Firefox 下生效).contain
的值是paint
(例如:contain: paint;
)
4.3 包含块参与计算
宽度:如果某元素的 width, left, right, padding, margin 属性,赋值为百分比值,那么这个数值是通过它的包含块的 width 属性值来计算。
高度:如果某元素的 height, top, bottom 属性,赋值为百分比值,那么这个数值是通过:
- 当它的包含块 height 值确定,则通过它的包含块的 height 属性值计算。
- 当它的包含块的 height 值会根据自身内容变化,而且包含块的
position
属性的值被赋予relative
或static
。那么这个数值是 'auto'。
参考:https://developer.mozilla.org/zh-CN/docs/Web/CSS/All_About_The_Containing_Block
4. float|CSS 2.2
float CSS 属性指定一个元素应沿其容器的左侧或右侧放置,允许 文本 和 内联元素 环绕它。该元素从网页的正常流动 (文档流) 中移除。
- 判定为浮动元素的标准:float 属性不是 none 的元素。float 通常会受 display 的影响。
- 浮动的定位方式:元素浮动后,会被移除正常流,然后向左/向右平移(在该行内块中移动), 一直平移到触碰容器边框 / 另一个浮动元素。
clear
clear
让一个元素移动到在它前面的那个浮动元素的下面。适用于自身是浮动的,或非浮动的元素。
-
针对 float 元素:可以让该 float元素不是按照 平移 的方式排到其他浮动元素后,而是另起一行,拍到第一个。
-
针对 非float 元素:可以让该元素的文本 / 内联元素,不是环绕 float元素。而是将该元素的边框边界,全部移到 float元素的下方。同时会发生外边距折叠。
-
none:不清除浮动
-
left:清除左浮动,向下移动。
-
right:清除右浮动,向下移动。
-
both:左右浮动全部清除,向下移动。
5. display|CSS 2.2
display 属性,通常有两对值,块级 / 行内级,两种都有对应关系。
5.1 作用
display
属性可以设置元素的内部和外部显示类型 display types。
5.1.1 outer display types
外部显示类型,决定了该元素处在正常流中的表现:是块级元素,还是内联级元素。
5.1.2 inner display types
内部显示类型,决定该元素的后代元素排版 / 布局方式:flow layout、grid、flex。
- display: static。默认:正常流 flow layout。遵循 CSS 2 系列的排版规则,是一个 BFC / IFC。
- display: flex。外部显示类型为block,内部显示类型为 flex。
- 该盒子的所有直接子元素都会成为 flex 元素,会根据弹性盒子(Flexbox)规则进行布局。
- display: grid。同上,内部会变成 grid 盒子。
5.2 取值
display 属性,可以改变该元素的显示类型,可取值如下(只列出较为通用的取值)
-
block
- 元素变成:块级元素 block-level element
-
inline
- 元素变成:行内级元素 inline-level element
-
inline-block
-
元素变成:行内块级元素 inline-block element
-
元素创建为盒子后,会变为原子行内级盒 atomic inline-level box,也就是说如果该行放不下该盒,就会空出剩余空间,该盒另起一行放置。
-
性质:内部显示类型为块级元素 block-level element,外部显示类型为行内级元素 inline-level element。
-
拥有部分 block 的性质:
- width、height 有效,可以设置盒子内容 content 的大小;
- padding、margin、border 会推开其他盒子。
-
拥有部分 inline 的性质:
- 盒子不会主动换行,多个内联块盒子会并排排放,和正常流一样,只有达到边界才会 “被迫” 换行;
-
问题:
- 如果设置了 width 内容宽度:则该盒子内的文本内容,会在盒内换行,不会横向溢出盒子。一但文本内容过多,就会纵向溢出盒子。
- 如果没有设置 width 内容宽度:则该盒子的宽度,会随着文本内容的增多而撑开。但是如果盒子边界达到容器宽度,文本内容会在盒子中换行。
-
总结:
inline-block
内联块盒,也就是说,是盒子之间内联关系的,内部块级的盒子。
-
-
-
list-item
-
元素变成:特殊的块级元素 block-level element
-
元素创建为盒子后:一个主块盒 + 一个标记盒。
-
-
none
- 会将元素从 可访问性树 accessibility tree 中移除,导致该元素及其所有子代元素不再被屏幕阅读技术 screen reading technology 访问。换句话说,它不仅仅是视觉隐藏,连屏幕阅读也无法读取了。
- 该元素不在格式化结构(formatting structure)中出现,即在视觉媒体中,元素和它的后代,均不会生成盒也不会影响排版。
-
flex 相关, grid相关,是 CSS3以后的属性值,在此不列出。
6. position|CSS 2.2
6.1 定义
CSS position
属性用于指定一个元素在文档中的定位方式。通过 position 确定定位方式,通过 top, right, bottom, left 属性确定偏移量。
偏移量的计算,是依照包含块,或自身原本位置为原始参考位置,然后盒偏移量(top, right, bottom, left 属性)参与计算得出。
position 来确定元素的定位位置,是依照 containing block 包含块决定的。
6.2 取值
- static
- 默认定位。
- 元素按照正常流的方式排版,此时盒偏移量(top, right, bottom, left 属性)无效。
- relative
- 相对定位。
- 元素的位置会先按照正常流方式排版,然后相对其在常规流中的位置进行偏移。
- 相对定位的盒子并不脱离正常流,它仍在常规流中保持占位。
- absolute
- 绝对定位。
- 按照相对于直系非 static 定位的父元素为参考,通过盒偏移量来确定位置。
- 绝对定位会脱离正常流。
- fixed
- 固定定位。
- 通常情况直接理解为相对于 视口 参考,通过盒偏移量来确定位置,下面是详细情况。
- 连续媒体 (continuous media) 的情况下:参考是适口 viewport;
- 分页媒体 (paged media) 下的情况下:参考是分页区域 (page area)。
- 固定定位会脱离正常流。
- sticky(CSS 3)
- 粘 性定位。
- 是 相对定位 和 固定定位 的混合。元素在跨越特定阈值(盒偏移量)前为相对定位,之后为固定定位。
- 元素根据正常文档流进行定位,然后相对它的 *最近滚动祖先(nearest scrolling ancestor)*和 containing block (最近块级祖先 nearest block-level ancestor),包括 table-related 元素。
- 基于盒偏移量值进行偏移。
如果引入包含块的概念:
- static:默认,按照正常流方式排版。
- relative:相对定位,先按照正常流方式排版,然后按照 包含块为基、盒偏移量为距离进行定位。
- absolute:绝对定位,先脱离正常流, 然后按照 包含块为基、盒偏移量为距离进行定位。
- fixed:固定定位,先脱离正常流,然后按照 包含块为基、盒偏移量为距离进行定位。
- 绝大多数情况下, fixed的包含块是视口。
参考:
7. 格式化上下文
名词:
- Formatting context:格式化上下文
- Block formatting context:块级格式化上下文,简称 BFC。
- Inline formatting context:行内级格式化上下文,简称 IFC。
- Table formatting context:表格格式化上下文,简称 TFC。
Block-level boxes participate in a BFC. 块级盒子参与 BFC。
Inline-level boxes participate in an IFC. 行 内级盒参与 IFC。
- line box 行盒:文字,或 inline-box 内联盒排出来的一整行盒,就是 line-box。
7.1 Block formatting context
块级格式化上下文,简称 BFC。
BFC 的概念,包括了它的元素内部的所有内容。BFC简单来讲,就是一套排版 / 渲染规则,它规定了块级元素的渲染方式:即:在一个BFC中,所有块级元素,会从包含块的内容块(content)顶部开始,在 垂直方向 依次排版。
BFC 对 float / clear 的影响:
- 浮动定位 和 清除浮动 时只会应用于同一个 BFC 内的元素。
- 浮动 不会影响其它 BFC 中元素的布局,而 清除浮动 只能清除同一 BFC 中在它 前面的元素 的浮动。
- 计算 BFC 的高度时,内部的浮动元素也要参与计算(解决 float 高度坍塌)。
BFC 对 外边距折叠的影响:
- 外边距折叠(Margin collapsing)只会发生在属于同一 BFC 的块级元素之间。
**注意:IFC、flex、grid都不会发生 Margin Collapse。**因为边距折叠只会发生在一个BFC中,如果创建了新的BFC,就不会发生边距折叠。
7.1.1 BFC 的生成条件:
判断依据:如果该元素是一个 block-level element,且这个元素的内部可以放一个正常流,就会生成 一个 BFC。
- 这句话拆分理解:
- 该元素必须是一个 block-level element:因为只有块级元素,才会生成块级格式化上下文 BFC。
- 该元素的内部可以放正常流:反情况理解,如果内部不能防止正常流(比如,替换元素内部不受 CSS 样式影响,table-row 内部必须放置 table-cell 等),那该元素的内部就不会生成一个 BFC,而是会放置特定的内容物。
具体的条件(5):
<html>
根元素。float
元素。position
absolute 元素。:fixed、absolute 固定定位、绝对定位的元素。display
block cintainers box:inline-block、list-item、table-cells、table-captions、fiex item、grid cells。overflow
非 visible 元素。overflow:hidden、scroll、auto、inherit。- overflow 属性是当内容移除元素边框时的处理方式。
7.1.2 BFC & margin callapse
如果没有 BFC:
如果两个盒子 A 和 B 在同一个 BFC 中,它们内部不存在新的 BFC,则 A 和 B 盒子内部会发生穿透:
<div id=container>
<div id="A">
<div class="box"></div>
<div class="box"></div>
</div>
<div id="B">
<div class="box"></div>
<div class="box"></div>
</div>
</div>
<style>
#A {
background-color: pink;
}
#B {
background-color: purple;
}
.box {
width: 100px;
height:100px;
margin: 20px 0px;
border: 1px solid black;
background-color: aqua;
}
</style>
效果:
图一:因为在粉色的 A 盒、紫色的 B 盒内部都不是 BFC,所以它们各自内部的 4 个 小盒子,即使不属于同一个父盒中,也互相发生了 margin callapse。
图二:而如果在 A 和 B 盒中建立各自的 BFC。通过分别为 A 和 B 添加 CSS 属性 overflow:auto
各自内部生成 BFC。此时它们互相之间便不再发生 margin callapse。A 和 B 两个盒子相互独立,内部不再相互受到干扰。
图三:形成对比的是,如果 A 盒不是 BFC,B 盒是一个BFC,则情况会如图3:A 盒内部没有被正确 “撑开”,发生了margin callapse,并且会穿透 A 盒与外界发生影响。而 B 盒是一个 BFC,在内部与外界线相互隔离,不受影响。
7.2 Inline formatting context
行内格式化上下文,就是网页中行内级元素 Inline-level element,会按照先后顺序,依次水平排列。如果遇到包含块的边界,就会另起一行,继续水平排列,直到排列结束。只要在这个包含块(上文)内,就是一个 IFC 行内格式化上下文。包含块通常是一个 Block container box。
7.2.1 line box
行盒。
上文介绍过,包含来自同一行所有盒的矩形区域叫做行盒 (line box)。所有行内级元素会从左至右依次排列,遇到包含块则再下一行继续排列。渲染的结果中,每一行就是一个 line box。
区分: line box 和 Inline box
- line box:
- 行盒。一个排版的概念,不会参与实际渲染。同一行的所有行内盒,会被一个行盒包括。无法通过 HTML 标签,或者 CSS 去定义。是一个在最终排版结果上看到的盒。
- Inline box:
- 行内盒。行内盒是行内级盒子中,由不可替换元素(non-replaced element) 生成的,且 dislpay 值是 'inline'的盒子。它参与行内格式化上下文。是 Inline-level box 的一个子集。
从结果上看,在一个行内格式化上下文中(水平书写模式):
- 所有 line box 垂直排列;
- 同一个 line box 中,所有的行内级元素水平排列。
如果是垂直书写模式,则对应 line box是水平排列;同一个 line box 中,所有行内级元素垂直排列。
7.2.3 细节
一个行内盒(inline box)被分割到多行中时, margins, borders, 以及 padding 的设定均不会在断裂处生效。 下例中有一个
<span>
元素,它包裹了一系列单词,占据了两行。可以看见在断裂处,<span>
的 border 同样发生了断裂。
7.3 其他补充
7.3.1 flow
前文介绍了 流内(in folw)、流外(out of flow)的知识。
in flow:流内。处在当前正常流中的元素,被称之为流内。
out of flow:流外。脱离当前正常流中的元素,被称之为流外。
在生成一个 FBC 时,
-
如果是 浮动元素、绝对定位元素,会脱离当前正常流,即发生流外。
-
如果是 block cintainers box,或者是 overflow 非默认的 visible 元素,便不会脱离当前正常流,依然在流内。
7.3.2 实例
直接摘录了 MDN 中的图片,如图,从元素结构上讲,浮动盒子是紫色盒子的子元素。当浮动盒子向左浮动时,出现了脱流紫盒子的正常流的情况(浮动盒子没有被紫盒子包裹 / 紫盒子没有被浮动盒子“上下撑开”)。
解决办法1:
- 因为float元素会生成新的BFC,同时它也会脱离正常流,浮动到它的包含块的左边。所以要把紫盒子定义为浮动盒子的“包含块”。常规做法是设置紫盒子为
overflow: auto
,只要可以令紫盒子变成浮动盒子的包含块,都可以。
使用
overflow
来创建一个新的 BFC,是因为overflow
属性告诉浏览器你想要怎样处理溢出的内容。当你使用这个属性只是为了创建 BFC 的时候,你可能会发现一些不想要的问题,比如滚动条或者一些剪切的阴影,需要注意。 —— MDN
解决办法2:
- CSS 3 中,在父级块中使用
display: flow-root
可以创建新的 BFC。同时,它规定该父级块内的所有子元素,都必须参与 BFC,即使是浮动元素。此时浮动元素的包含块就是这个父级块。
关于值
flow-root
的这个名字,当你明白你实际上是在创建一个行为类似于根元素 (浏览器中的<html>
元素) 的东西时,就能发现这个名字的意义了——即创建一个上下文,里面将进行 flow layout。 —— MDN
参考